package nrpc.client.skst;

import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import nrpc.client.serst.ServiceNode;
import nrpc.exception.OutOfLimitationSocketException;

public class PoolSocketStrategy implements SocketStrategy {

	private static final int INIT = 10, MAX = 100;
	
	private Map<ServiceNode, Pool> map = new HashMap<>();
	
	private class Pool{
		
		private ArrayList<SocketChannel> all = new ArrayList<>();
		private ArrayList<SocketChannel> freeList = new ArrayList<>();
		private ServiceNode node;
		private int maxCount;
		private int total;
		
		public Pool(ServiceNode node, int initCount, int maxCount) {
			all.ensureCapacity(maxCount);
			freeList.ensureCapacity(maxCount);
			this.node = node;
			this.maxCount = maxCount;
			for(int i = 0; i<initCount; i++)
				createNew();
		}
		
		public void createNew(){
			try {
//				 if(total == maxCount) {
//					throw new OutOfLimitationSocketException("超出最大连接数");
//				}
				var n = SocketChannel.open(node.getAddress());
				all.add(n);
				freeList.add(n); 
				this.total++;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public synchronized void back(SocketChannel socketChannel){
			this.freeList.add(socketChannel);
			this.notify();
		}
		
		public synchronized SocketChannel pop(){
			if(freeList.size()==0){
				if(total < maxCount)
					createNew();
				else try {
						this.wait();
					} catch (InterruptedException e) {}
			}
			return freeList.remove(freeList.size()-1);
		}
		
		public void close(){
			all.forEach(t -> {
				try {
					t.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			});
		}
	}
	
	@Override
	public synchronized SocketChannel getChannel(ServiceNode node) throws IOException {
		var x = map.get(node);
		if(x == null){
			x = new Pool(node, INIT, MAX);
			map.put(node, x);
		}
		return x.pop();
	}

	@Override
	public synchronized void backChannel(ServiceNode node, SocketChannel channel) {
		map.get(node).back(channel);
	}

	@Override
	public synchronized void disconnect(ServiceNode node) {
		map.remove(node).close();
	}

	@Override
	public synchronized void close() {
		map.forEach((n, p)->{
			p.close();
		});
	}
	
}
